package cn.ulyer.common.spring;

import cn.ulyer.common.annotation.Decrypt;
import cn.ulyer.common.transcode.secret.DecryptCompositeBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class DecryptParameterResolver implements HandlerMethodArgumentResolver {

    @Autowired
    private RequestMappingHandlerAdapter requestMappingHandlerAdapter;

    @Autowired
    private DecryptCompositeBean decryptCompositeBean;

    final Map<String,HandlerMethodArgumentResolver> resolverCache = new ConcurrentHashMap<>();

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(Decrypt.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        String contentType = webRequest.getHeader(HttpHeaders.CONTENT_TYPE);
        HandlerMethodArgumentResolver cacheResolver = resolverCache.get(contentType);
        Object parameterObject = null;
        if(cacheResolver!=null){
            parameterObject = cacheResolver.resolveArgument(parameter,mavContainer,webRequest,binderFactory);
        }
        if(cacheResolver == null){
            List<HandlerMethodArgumentResolver> resolverList = requestMappingHandlerAdapter.getArgumentResolvers();
           switch (contentType){
               case MediaType.APPLICATION_JSON_VALUE:
                   for (HandlerMethodArgumentResolver resolver : resolverList) {
                       if(resolver instanceof RequestResponseBodyMethodProcessor){
                           parameterObject = resolver.resolveArgument(parameter,mavContainer,webRequest,binderFactory);
                           resolverCache.put(contentType,resolver);
                           break;
                       }
                   }
                   break;
               case MediaType.APPLICATION_FORM_URLENCODED_VALUE:
                   for (HandlerMethodArgumentResolver resolver : resolverList) {
                       if(resolver instanceof ServletModelAttributeMethodProcessor){
                           parameterObject = resolver.resolveArgument(parameter,mavContainer,webRequest,binderFactory);
                           resolverCache.put(contentType,resolver);
                           break;
                       }
                   }
                   break;
               default:
                   for (HandlerMethodArgumentResolver resolver : resolverList) {
                       if(resolver.supportsParameter(parameter)&&!(resolver instanceof DecryptParameterResolver)){
                           parameterObject = resolver.resolveArgument(parameter,mavContainer,webRequest,binderFactory);
                           resolverCache.put(contentType,resolver);
                           break;
                       }
                   }

           }
        }
        if(parameterObject!=null){
            Decrypt decrypt = parameter.getParameterAnnotation(Decrypt.class);
            // maybe throw decryptException 解密出错
            parameterObject = decryptCompositeBean.decrypt(parameter,parameterObject,decrypt,webRequest);
        }
        return parameterObject;
    }





}
